home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The X-Philes (2nd Revision)
/
The X-Philes Number 1 (1995).iso
/
xphiles
/
psion
/
go.z
/
go
/
go.opl
< prev
next >
Wrap
Text File
|
1993-07-29
|
14KB
|
837 lines
REM GO EDITOR using John Hind's application framework
REM Copyright (C) 1993 John Tromp
APP Go
TYPE 3
PATH "\GO"
EXT "GO"
ICON "\GO\Go.ico"
ENDA
PROC Go:
GLOBAL bw%,bh%,bs% REM board width,height,size
GLOBAL gb% REM go board, stone bitmap
GLOBAL eb%,db% REM empty/dot bitmaps
GLOBAL sr%,sd%,nd% REM stone radius/diameter, neighbour distance
GLOBAL bwp%,bhp% REM board width/height in pixels
GLOBAL maxnv% REM max # non-visible pixels
GLOBAL mv%(512),mn% REM moves, move number
GLOBAL bd%(880) REM board data sets
GLOBAL cx%,cy% REM cursor x/y
GLOBAL pt%(440) REM point type
GLOBAL uf%(440) REM union find
GLOBAL log%(4096) REM union/find log
GLOBAL clix% REM log index
GLOBAL ogb%,osb% REM overview go board/stones
GLOBAL obwp%,obhp% REM overview board width/height
GLOBAL komi REM 2nd move compensation
GLOBAL name$(128) REM file name
GLOBAL changed% REM remember to save
GLOBAL ten% REM prepare for rank>=10
GLOBAL ko%(512) REM forbidden moves
LOCAL x%,y%
db%=gCREATEBIT(4,4)
gCLS
gAT 1,1 :gLINEBY 1,0
osb%=gCREATEBIT(15,5)
gCLS :gAT 2,2 :gLINEBY 1,0
gAT 5,0 :gINVERT 5,5
gAT 10,0 :gINVERT 5,5
gAT 11,1 :gFILL 3,3,1
komi=5.5
log%(1)=0
x%=22 :y%=22
pt%(x%)=3
pt%(x%-21)=-1 :pt%(y%-1)=-1
DO
x%=x%+1 :y%=y%+21
pt%(x%-21)=-1 :pt%(y%-1)=-1
pt%(x%)=4 :pt%(y%)=6
UNTIL x%=41
sr%=3 :maxnv%=15
bw%=9 :bh%=9
newstns:
LOADM "\OPO\FRAMELIB.OPO" REM Load the Application Framework code
fAutoOff: REM Allow automatic switch-off
fRun:($330,"LSBEPNDTOKRCX",0) REM Run application
ENDP
PROC aHkX%: REM Callback to exit application on PSION-X
RETURN 100 REM "Exit from application" message
ENDP
PROC aMh5%: REM Callback for "Printable key pressed" message
IF fParm%=32
forward%:
RETURN 0
ENDIF
IF fParm%=49 AND bh%>=10
torow:(ten%+1)
ten%=10-ten%
ELSEIF fParm% >= 48 AND fParm% < 58
torow:(ten%+fParm%-48)
ten%=0
ELSE
fParm%=fParm% AND $FFDF
IF fParm%>=%A AND fParm%<=%T
tocol:(fParm%-%A-(fParm%<%J))
ten%=0
ELSE
BEEP 2,300
ENDIF
ENDIF
RETURN 0
ENDP
PROC aMh6%: REM Callback for "Special key pressed" message
IF fParm%=$100 :up:
ELSEIF fParm%=$101 :down:
ELSEIF fParm%=$102 :right:
ELSEIF fParm%=$103 :left:
ELSEIF fParm%=8 :back:
ELSEIF fParm%=13 :move:
ELSE BEEP 2,300
ENDIF
RETURN 0
ENDP
PROC aMh9%: REM Callback for "Menu key pressed" message
LOCAL k%
mINIT
mCARD "File","Load",%L,"Save as",%S
mCARD "Play","Begin",%B,"End",%E,"Pass",%P,"Notate",%N
mCARD "Display","Dimensions",%D,"Scrolling",%T,"Overview",%O
mCARD "Special","Komi",%K,"Remove",%R,"Count",%C,"Exit",%x
k%=fMenu%: REM Show menu (NOTE: bug fixed version of MENU)
IF k%=0 REM Menu aborted by user
RETURN 0 REM Return null command
ELSE
fParm%=k% REM Parameter for "Hotkey pressed" message
RETURN 4 REM "Hotkey pressed" message
ENDIF
ENDP
PROC aMh10%: REM Callback for "Help key pressed" message
dINIT "Help: Go Editor"
dTEXT "","Use cursor keys to move"
dTEXT "","or type the coordinates"
dTEXT "","Shift increases cursor movement"
dTEXT "","Enter to place stone"
dTEXT "","Delete/Space to go back/forward"
fLock:
DIALOG
fUnlock:
RETURN 0
ENDP
PROC aHkN%:
GIPRINT CHR$(cx%+%A+(cx%<10))+GEN$(bh%+1-cy%,2)
RETURN 0
ENDP
PROC aHkK%:
dINIT "Set komi"
dFLOAT komi,"Komi:",0,256
fLock:
DIALOG
fUnlock:
RETURN 0
ENDP REM setkomi
PROC aHkC%:
LOCAL p%,s%,ws
GLOBAL wc%,bc%
GIPRINT "Counting..."
p%=pos%:(bw%,bh%)
DO
s%=bd%(p%)
IF s%=0 :s%=4 :ENDIF
bd%(p%+440)=s%
p%=p%-1
UNTIL p%<21
p%=pos%:(bw%,bh%)
DO
DO
s%=bd%(p%+440)
IF s%=4
s%=whose%:(p%)
IF s%=1 OR s%=2
assign:(p%,s%)
ENDIF
ENDIF
IF s%=1
bc%=bc%+1
ELSEIF s%=2
wc%=wc%+1
ENDIF
p%=p%-1
UNTIL pt%(p%)=-1
p%=p%+bw%-20
UNTIL p%<21
overvw:(440)
ws=wc%+komi
dINIT "Score"
dTEXT "White:",GEN$(ws,5)
dTEXT "Black:",GEN$(bc%,5)
IF ws>bc%
dTEXT "","White wins"
ELSEIF bc%>ws
dTEXT "","Black wins"
ELSE dTEXT "","Jigo"
ENDIF
fLock:
DIALOG
fUnlock:
RETURN 0
ENDP REM count
PROC torow:(r%)
IF r%>=1 AND r%<=bh%
cy%=bh%+1-r%
mvcur:
ENDIF
ENDP
PROC tocol:(c%)
IF c%>=1 AND c%<=bw%
cx%=c%
mvcur:
ENDIF
ENDP
PROC whose%:(p%)
LOCAL s%
IF pt%(p%)=-1 :RETURN 0 :ENDIF
s%=bd%(p%+440)
IF s%<4 :RETURN s% :ENDIF
bd%(p%+440)=0
RETURN whose%:(p%-21) OR whose%:(p%+1) OR whose%:(p%+21) OR whose%:(p%-1)
ENDP REM whose
PROC assign:(p%,s%)
IF pt%(p%)=-1 OR bd%(p%+440) :RETURN :ENDIF
bd%(p%+440)=s%
assign:(p%-21,s%)
assign:(p%+1,s%)
assign:(p%+21,s%)
assign:(p%-1,s%)
ENDP REM assign
PROC aHkL%:
LOCAL ret%
name$="\GO\*.go"
dINIT "Load file"
dFILE name$,"File:",0
fLock:
ret%=DIALOG
fUnlock:
IF ret%=0 :RETURN 0 :ENDIF
fParm$=name$
RETURN 102
ENDP
PROC aOpen%: REM Callback for file opening
LOCAL ret%,fh%,x%,y%
name$=fParm$
ret%=IOOPEN(fh%,name$,0)
IF ret%<0
GIPRINT ERR$(ret%)
RETURN -3
ENDIF
ret%=IOREAD(fh%,ADDR(mv%()),2)
x%=PEEKB(ADDR(mv%()))
y%=PEEKB(ADDR(mv%())+1)
IF x%<2 OR x%>19 OR y%<2 OR y%>19
GIPRINT "Illegal board size"
IOCLOSE(fh%)
RETURN -3
ENDIF
resize:(x%,y%)
ret%=IOREAD(fh%,ADDR(mv%()),1024)
IF ret%=-36
ret%=0 :REM premature eof (bug)
ENDIF
GIPRINT GEN$(ret%/2,3)+" moves read"
IOCLOSE(fh%)
changed%=0
RETURN 0
ENDP
PROC aCreate%: REM Callback for file creation
name$=fParm$
changed%=1
aHkD%:
RETURN 0
ENDP
PROC aClose%: REM Calback for file closing
LOCAL ret%
IF changed%
fLock:
ret%=ALERT("Save changes?","","No","Yes")
fUnlock:
IF ret%=2
aHkS%:
ENDIF
ENDIF
RETURN 0
ENDP
PROC aHkS%:
LOCAL bytes%,m%,i&,fh%
m%=0
WHILE mv%(m%+1) :m%=m%+1 :ENDWH
i&=mn%
dINIT "Save file"
dFILE name$,"Name:",17
dLONG i&,"Moves:",0,m%
fLock:
m%=DIALOG
fUnlock:
IF m%=0
GIPRINT "Not saved"
RETURN 0
ENDIF
IF UPPER$(RIGHT$(name$,3))<>".GO"
name$=name$+".go"
ENDIF
m%=IOOPEN(fh%,name$,$102)
IF m%<0
GIPRINT ERR$(m%)
RETURN -1
ENDIF
bytes%=256*bh%+bw%
IOWRITE(fh%,ADDR(bytes%),2)
bytes%=2*i&
m%=IOWRITE(fh%,ADDR(mv%()),bytes%)
IF m%<0
GIPRINT ERR$(m%)
RETURN -1
ENDIF
GIPRINT "Game saved"
changed%=0
IOCLOSE(fh%)
RETURN 0
ENDP REM save
PROC aHkO%:
overvw:(0)
RETURN 0
ENDP
PROC overvw:(off%)
LOCAL dx%,y%,p%,s%
gUSE ogb%
gCLS
y%=1
DO
dx%=0 :gAT 0,4*(y%-1)
p%=pos%:(1,y%)
DO
s%=5*bd%(p%+dx%+off%)
gCOPY osb%,s%,0,5,5,0
dx%=dx%+1 :gMOVE 4,0
UNTIL dx%=bw%
y%=y%+1
UNTIL y%>bh%
gUSE gb%
ENDP REM overvw
PROC aHkB%:
emptybrd:(0)
drawbrd:
RETURN 0
ENDP REM start
PROC emptybrd:(off%)
LOCAL i%
i%=1
DO
bd%(i%+off%)=0
i%=i%+1
UNTIL i%>440
clix%=1 :mn%=0
ENDP REM emptybrd
PROC up:
IF cy%>1
IF fKmod% AND 2
cy%=MAX(cy%-6,1)
ELSE
cy%=cy%-1
ENDIF
mvcur:
ENDIF
ENDP REM up
PROC down:
IF cy%<bh%
IF fKmod% AND 2
cy%=MIN(cy%+6,bh%)
ELSE
cy%=cy%+1
ENDIF
mvcur:
ENDIF
ENDP REM down
PROC right:
IF cx%<bw%
IF fKmod% AND 2
cx%=MIN(cx%+6,bw%)
curret:
ELSE
cx%=cx%+1
gMOVE nd%,0
ENDIF
ENDIF
ENDP REM right
PROC left:
IF cx%>1
IF fKmod% AND 2
cx%=MAX(cx%-6,1)
curret:
ELSE
cx%=cx%-1
gMOVE -nd%,0
ENDIF
ENDIF
ENDP REM left
PROC move:
GLOBAL root%
LOCAL p%,s%,ret%,cap%
p%=pos%:(cx%,cy%)
IF bd%(p%) OR p%=ko%(mn%+1)
BEEP 9,100
RETURN
ENDIF
IF fParm%=13
changed%=1
ENDIF
mn%=mn%+1
s%=2-(mn% AND 1)
play:(cx%,cy%,s%)
root%=p% :ufset:(root%,0)
ko%(mn%+1)=0
cap%=neighbr%:(p%-21,cx%,cy%-1,s%)+2*neighbr%:(p%+1,cx%+1,cy%,s%)+4*neighbr%:(p%+21,cx%,cy%+1,s%)+8*neighbr%:(p%-1,cx%-1,cy%,s%)
IF uf%(root%)=0
capture%:(cx%,cy%,s%)
cap%=16
ENDIF
mv%(mn%)=cx%+256*cy%
clix%=clix%+1
log%(clix%)=cap%
curret:
ENDP REM move
PROC neighbr%:(p%,x%,y%,s%)
LOCAL ns%,nr%
IF pt%(p%)=-1 :RETURN 0: ENDIF
ns%=bd%(p%)
IF ns%=0
ufset:(root%,uf%(root%)-1) REM add liberty
ko%(mn%+1)=-1
RETURN 0
ENDIF
nr%=p%
WHILE uf%(nr%)>0
nr%=uf%(nr%)
ENDWH
IF ns%=s%
IF nr%=root%
ufset:(nr%,uf%(nr%)+1)
ELSE
ns%=uf%(root%)+uf%(nr%)+1
IF uf%(root%) < uf%(nr%)
ufset:(root%,ns%)
ufset:(nr%,root%)
ELSE
ufset:(nr%,ns%)
ufset:(root%,nr%)
root%=nr%
ENDIF
ENDIF
ko%(mn%+1)=-1
RETURN 0
ENDIF
IF uf%(nr%)=-1
IF capture%:(x%,y%,ns%)=1 AND ko%(mn%+1)=0
ko%(mn%+1)=p%
ELSE
ko%(mn%+1)=-1
ENDIF
RETURN 1
ENDIF
ufset:(nr%,uf%(nr%)+1)
RETURN 0
ENDP REM neighbr
PROC ufset:(i%,v%)
clix%=clix%+2
log%(clix%)=i%
log%(clix%-1)=uf%(i%)
uf%(i%)=v%
ENDP REM ufset
PROC back:
LOCAL p%,s%,i%
IF mn%=0 :BEEP 10,100 :RETURN :ENDIF
p%=mv%(mn%)
s%=2-(mn% AND 1)
mn%=mn%-1
IF p%=-1
GIPRINT "Pass"
RETURN
ENDIF
cx%=PEEKB(ADDR(mv%(mn%+1)))
cy%=PEEKB(ADDR(mv%(mn%+1))+1)
p%=pos%:(cx%,cy%)
mvcur:
i%=log%(clix%)
clix%=clix%-1
IF i%=16
fill:(cx%,cy%,s%)
ELSE
play:(cx%,cy%,s%)
IF i% AND 1 :fill:(cx%,cy%-1,3-s%) :ENDIF
IF i% AND 2 :fill:(cx%+1,cy%,3-s%) :ENDIF
IF i% AND 4 :fill:(cx%,cy%+1,3-s%) :ENDIF
IF i% AND 8 :fill:(cx%-1,cy%,3-s%) :ENDIF
ENDIF
DO
i%=log%(clix%)
IF i%<20 :BREAK :ENDIF
uf%(i%)=log%(clix%-1)
clix%=clix%-2
UNTIL 0
play:(cx%,cy%,0)
curret:
ENDP REM back
PROC aHkE%:
IF forward%:
RETURN 0
ENDIF
RETURN 4
ENDP
PROC forward%:
LOCAL p%,x%,y%
mn%=mn%+1
p%=mv%(mn%)
IF p%=-1
GIPRINT "Pass"
ELSE
x%=PEEKB(ADDR(mv%(mn%)))
y%=PEEKB(ADDR(mv%(mn%))+1)
p%=pos%:(x%,y%)
mn%=mn%-1
IF x%=0 OR x%>bw% OR y%=0 OR y%>bh%
BEEP 4,400
RETURN -1
ENDIF
IF bd%(p%) OR p%=ko%(mn%+1)
BEEP 7,100
RETURN -1
ENDIF
cx%=x% :cy%=y%
mvcur:
move:
ENDIF
RETURN 0
ENDP REM forward
PROC fill:(x%,y%,s%)
LOCAL p%
p%=pos%:(x%,y%)
IF bd%(p%) OR pt%(p%)=-1 :RETURN :ENDIF
play:(x%,y%,s%)
fill:(x%,y%-1,s%)
fill:(x%+1,y%,s%)
fill:(x%,y%+1,s%)
fill:(x%-1,y%,s%)
ENDP REM fill
PROC aHkR%:
capture%:(cx%,cy%,bd%(pos%:(cx%,cy%)))
curret:
RETURN 0
ENDP REM delete
PROC capture%:(x%,y%,col%)
LOCAL p%,s%
p%=pos%:(x%,y%)
s%=bd%(p%)
IF s%=0 OR pt%(p%)=-1 :RETURN 0 :ENDIF
IF s%<>col%
WHILE uf%(p%)>0
p%=uf%(p%)
ENDWH
ufset:(p%,uf%(p%)-1)
RETURN 0
ENDIF
play:(x%,y%,0)
p%=1+capture%:(x%,y%-1,col%)+capture%:(x%+1,y%,col%)+capture%:(x%,y%+1,col%)+capture%:(x%-1,y%,col%)
RETURN p%
ENDP REM capture
PROC aHkP%:
changed%=1
mn%=mn%+1
mv%(mn%)=-1
ko%(mn%+1)=0
GIPRINT "Pass"
RETURN 0
ENDP REM pass
PROC aHkT%:
LOCAL maxnv&
maxnv&=maxnv%
dINIT "Scrolling"
dLONG maxnv&,"Edge visibility:",0,39
fLock:
DIALOG
fUnlock:
IF maxnv&<>maxnv%
maxnv%=maxnv&
vscroll:
ENDIF
RETURN 0
ENDP REM scroll
PROC drawbrd:
LOCAL p%,dx%,y%,s%
gUPDATE OFF
gCLS
IF sr%=1
drawdots:
ELSE
drwlines:
ENDIF
y%=1
DO
dx%=0 :p%=pos%:(1,y%)
DO
s%=bd%(p%+dx%)
IF s% OR pt%(p%+dx%)=7
play:(dx%+1,y%,s%)
ENDIF
dx%=dx%+1
UNTIL dx%=bw%
y%=y%+1
UNTIL y%>bh%
mvcur:
CURSOR gb%,0,3,3
gUPDATE ON
ENDP REM drawbrd
PROC drawdots:
gPATT db%,bwp%,bhp%,3
ENDP REM drawdots
PROC drwlines:
LOCAL i%
i%=sr%
DO
gAT i%,sr%
gLINEBY 0,bhp%-sd%+1
i%=i%+nd%
UNTIL i%>=bwp%
i%=sr%
DO
gAT sr%,i%
gLINEBY bwp%-sd%,0
i%=i%+nd%
UNTIL i%>=bhp%
ENDP REM drwlines
PROC play:(x%,y%,s%)
LOCAL p%,t%
p%=pos%:(x%,y%)
bd%(p%)=s%
bat:(x%,y%)
t%=s%
IF t%=0
t%=pt%(p%)
ENDIF
gCOPY eb%,t%*sd%,0,sd%,sd%,3
ENDP REM play
PROC aHkD%:
LOCAL bw&,bh&,neww%,newh%,oldsr%,ret%
bw&=bw%
bh&=bh%
oldsr%=sr%
dINIT "Dimensions"
dLONG bw&,"Board Width:",2,19
dLONG bh&,"Board Height:",2,19
dCHOICE sr%,"Stone Size:","3,5,7,9,11"
fLock:
ret%=DIALOG
fUnlock:
IF ret%=0 :RETURN 0 :ENDIF
neww%=bw& :newh%=bh&
IF oldsr%<>sr%
newstns:
ENDIF
IF neww%<>bw% OR newh%<>bh% OR bs%=0
resize:(neww%,newh%)
ELSE
newbrd:
ENDIF
RETURN 0
ENDP REM board
PROC newstns:
LOCAL i%
sd%=2*sr%+1
nd%=sd%+1
IF eb% :gCLOSE(eb%) :ENDIF
eb%=gCREATEBIT(12*nd%,nd%)
gCLS
IF sr%=1
i%=1
DO
gAT i%,1 :gLINEBY 1,1
i%=i%+3
UNTIL i%>36
gAT 3,0 :gFILL 3,3,1 :gINVERT 3,3
gAT 6,0 :gFILL 3,3,2
RETURN
ENDIF
gAT sr%,0 :gLINEBY 0,sd%
gAT 0,sr% :gLINEBY sd%,0
gAT 3*sd%+sr%,sr% :gBOX sd%+nd%,sd%
gAT 4*sd%+sr%,sr% :gLINEBY 0,sd%
gAT 6*sd%+sr%,0 :gLINEBY 0,nd%
gAT 7*sd%+sr%,0 :gLINEBY 0,nd%
gAT 8*sd%+sr%,0 :gLINEBY 0,nd%
gAT 6*sd%+sr%,sr% :gLINEBY 2*sd%,0
gAT 9*sd%+sr%,-sr% :gBOX sd%+nd%,sd%
gAT 10*sd%+sr%,0 :gLINEBY 0,sr%
IF sr%=2
gAT 7*sd%+sr%,sr% :gFILL 1,1,1
ELSE
gAT 7*sd%+sr%-1,sr%-1 :gFILL 3,3,0
ENDIF
gAT sd%,0
IF sr%<4
gINVERT sd%,sd%
ELSE
gBORDER 0,sd%,sd%
gMOVE 1,1
gFILL sd%-2,sd%-2,0
ENDIF
gAT 2*sd%,0
IF sr%<4
gINVERT sd%,sd%
gMOVE 1,1
gFILL sd%-2,sd%-2,1
ELSE
gBORDER 0,sd%,sd%
ENDIF
ENDP REM newstns
PROC border:(w%,h%,m%)
LOCAL i%,e%
i%=pos%:(1,h%)
pt%(i%)=6-3*m%
pt%(i%+21)=m% OR 6
e%=i%+w%-1
WHILE i%<e%
i%=i%+1
pt%(i%)=10 AND m%
pt%(i%+21)=-1 AND m%
ENDWH
i%=21+w%
pt%(i%)=(m% AND 5) OR 4
pt%(i%+1)=m% OR 4
WHILE i%<e%
i%=i%+21
pt%(i%)=8 AND m%
pt%(i%+1)=-1 AND m%
ENDWH
pt%(e%)=11 AND m%
ENDP REM border
PROC resize:(w%,h%)
LOCAL bs9%,bs13%,bs19%
IF bw% :border:(bw%,bh%,0) :ENDIF
bw%=w% :bh%=h%
bs%=bw%*bh%
obwp%=4*bw%+1 :obhp%=4*bh%+1
IF ogb% :gCLOSE(ogb%) :ENDIF
ogb%=gCREATE(240-obwp%,0,obwp%,obhp%,1)
bs9%=(bw%=9) AND (bh%=9) AND 7
bs13%=(bs%=169) AND 7
bs19%=(bs%= 361) AND 7
pt%(pos%:( 3, 3))=bs9%
pt%(pos%:( 3, 7))=bs9%
pt%(pos%:( 7, 3))=bs9%
pt%(pos%:( 7, 7))=bs9% OR bs13%
pt%(pos%:( 4, 4))=bs13% OR bs19%
pt%(pos%:( 4,10))=bs13% OR bs19%
pt%(pos%:( 4,16))=bs19%
pt%(pos%:(10, 4))=bs13% OR bs19%
pt%(pos%:(10,10))=bs13% OR bs19%
pt%(pos%:(10,16))=bs19%
pt%(pos%:(16, 4))=bs13% OR bs19%
pt%(pos%:(16,10))=bs19%
pt%(pos%:(16,16))=bs13% OR bs19%
border:(bw%,bh%,-1)
IF bw%>=6 AND bh%>=6
cx%=4 :cy%=4
ELSE
cx%=1 :cy%=1
ENDIF
emptybrd:(0)
newbrd:
ENDP REM resize
PROC newbrd:
bwp%=bw%*nd%-1
bhp%=bh%*nd%-1
IF gb%
gCLOSE(gb%)
ENDIF
gb%=gCREATE(0,0,bwp%,bhp%,1)
drawbrd:
ENDP REM newbrd
PROC bat:(x%,y%)
gAT (x%-1)*nd%,(y%-1)*nd%
ENDP REM bat
PROC curret:
gAT cx%*nd%-sr%-3,cy%*nd%-sr%-3
ENDP REM curret
PROC mvcur:
curret:
vscroll:
ENDP REM mvcur
PROC vscroll:
LOCAL nv%
nv%=gY-39
IF bhp%<=80 :RETURN :ENDIF
IF nv%<=maxnv%
gSETWIN 0,0
ELSEIF bhp%-80-nv%<=maxnv%
gSETWIN 0,80-bhp%
ELSE
gSETWIN 0,-nv%
ENDIF
ENDP REM vscroll
PROC pos%:(x%,y%)
RETURN x%+21*y%
ENDP REM pos%